// locutor
// (c) 2008-2009, Normen Müller, normen.mueller@gmail.com  
// $Id: Parser.scala 2536 2009-10-26 21:22:28Z nmueller $
package locutor.model.parsing.dm

import java.io.File

import scala.util.matching.Regex
import scala.util.parsing.combinator._
import scala.util.parsing.combinator.syntactical._

import scaup.Views._

import locutor.model.parsing.grgen.{GM, T, GRG, R}

case class TR(es: Option[List[TR]], t: T, r: R)

/** 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.5 */
private [dm] class Parser extends StdTokenParsers with ImplicitConversions {
  /* Fill in abstract defs*/
  type Tokens = Lexer
  val lexical: Tokens = new Lexer
  
  /* Configure lexical parsing */                                 
  lexical.reserved ++= List("model", "extends", "nodes", "edges", "equivs", "rules")
  lexical.delimiters ++= List("{", "}", ",")
  
  /* Define the grammar */
  def dm = ("model" ~> ident) ~ opt("extends" ~> extends_stmt) ~ ("{" ~> stmts <~ "}") ^^ {
    case name ~ exts ~ (T(_, nodes) ~ T(_, edges) ~ equivs ~ rules) => TR(exts, T(name, nodes ::: edges), rules)
  }

  def extends_stmt = ident ~ (("," ~> ident)*) ^^ {
    case m ~ ms => (m :: ms) map (m => DM("etc/models".toFile / m.toLowerCase / (m.toLowerCase+".dm")))
  }

  def stmts = nodes ~ edges ~ equivs ~ rules
  
  def nodes = ("nodes" ~ "{") ~> ((line)*) <~ "}" ^^ {
    case lines => 
      println("nodes: ")
      lines foreach println
      GM(lines mkString)
  }

  def edges = ("edges" ~ "{") ~> ((line)*) <~ "}"  ^^ {
    case lines => GM(lines mkString)
  }

  def equivs = ("equives" ~ "{") ~ ((line)*) ~ "}" ^^^ {Nil}

  def rules = ("rules" ~ "{") ~> ((line)*) <~ "}"  ^^ {
    case lines => GRG(lines mkString)
  }

  def line = """.*""".r
  
  def string = accept("string",  {case lexical.StringLit(n) => n})

  def numeric = accept("numeric", {case lexical.NumericLit(n) => n.toInt})

  // =====================================================================================================================================
  private val whiteSpace = """\s+""".r

  private def skipWhitespace = whiteSpace.toString.length > 0

  private def handleWhiteSpace(source: java.lang.CharSequence, offset: Int): Int =
    if (skipWhitespace)
      (whiteSpace findPrefixMatchOf (source.subSequence(offset, source.length))) match {
        case Some(matched) => offset + matched.end
        case None => offset
      }
    else 
      offset

  implicit def regex(r: Regex): Parser[String] = new Parser[String] {
    def apply(in: Input) = {
      val source = in.source
      val offset = in.offset
      val start = handleWhiteSpace(source, offset)
      (r findPrefixMatchOf (source.subSequence(start, source.length))) match {
        case Some(matched) =>
          Success(source.subSequence(start, start + matched.end).toString, in.drop(start + matched.end - offset))
        case None =>
          Failure("string matching regex `"+r+"' expected but `"+in.first+"' found", in.drop(start - offset))
      }
    }
  }
}
